<template>
  <el-dialog
    :modal-class="center?null : 'not_center'"
    :close-on-click-modal="modalClose"
    :style="{'user-select':'none'}"
    :title="formTitle"
    :top="styleValue(top,'vh','6vh')"
    v-model="showDialogRef"
    :width="styleValue(width,'%','50%')"
    append-to-body
    @close="resetForm"
  >
    <template v-if="$slots['dialog-title']" #header>
      <div class="dialog-title">
        <slot name="dialog-title" :title="formTitle" :row="formRef" />
      </div>
    </template>

    <!--表单上方插槽-->
    <template v-if="$slots.header">
      <slot name="header" />
    </template>
    <div v-if="tabs.length>1" class="tab">
      <el-tabs v-model="curTabIndexRef" type="card">
        <template v-for="(item, index) in tabs" :key="index">
          <el-tab-pane v-if="shouldShowItem(item)&&weakShowItem(item)" :key="index" :label="item.name||item" :name="String(index)" />
        </template>
      </el-tabs>
    </div>
    <el-scrollbar
      ref="scrollbar"
      :class="{'edit-form':tabs.length===1,'tabs-form':tabs.length>1}"
      :style="{height:styleValue(height,'vh')}"
    >
      <div :class="{'have-scrollbar':formProps.length>1,'no-scrollbar':formProps.length<=1}">
        <!--表单-->
        <el-form ref="formTemplateRef" :label-width="styleValue(labelWidth,'rem')" :model="formRef" @validate="handleValidate">
          <template v-for="(group,groupIndex) in formItemsRef" :key="groupIndex">
            <!--表单组-->
            <div v-show="Number(openTabIndexRef) === groupIndex">
              <!--如果是tabs则进行分页,否则不分页直接展示-->
              <template v-for="(formItem, itemIndex) in group" :key="itemIndex+'_'+groupIndex">
                <template v-if="tabs.length>0?shouldShowItem(tabs[groupIndex])&&weakShowItem(tabs[groupIndex]):true">
                  <!--表单组内容-->
                  <el-row>
                    <template v-for="(item, index) in formItem">
                      <!--表单组内容插槽-->
                      <slot v-if="item.slot&&shouldShowItem(item)" :name="item.slot" :row="formRef" />
                      <!--表单项(不使用插槽且显示条件为true时渲染)-->
                      <el-col
                        v-if="!item.slot && item.prop && shouldShowItem(item)&&weakShowItem(item)"
                        :key="index"
                        :span="item.span||24/formItem.filter(i=>shouldShowItem(i)&&weakShowItem(i)).length"
                      >
                        <el-form-item
                          :label="itemLabel(item)"
                          :label-width="styleValue(item.labelWidth,'rem')"
                          :prop="itemProp(item)"
                          :rules="computedRules[itemProp(item)]"
                          :style="item.style"
                        >
                          <!--表单项插槽-->
                          <template v-if="item.type?.name === TypeName.slot && showDialogRef">
                            <slot :name="item.prop" :row="formRef" />
                          </template>
                          <!--文本输入框-->
                          <template v-else-if="item.type?.name === TypeName.input">
                            <el-input
                              v-model="formRef[item.prop]"
                              :autosize="item.type.autosize"
                              :disabled="shouldDisabled(item)"
                              :max="item.type.max"
                              :clearable="item.required===false"
                              :maxlength="item.type.maxlength"
                              :min="item.type.min"
                              :minlength="item.type.minlength"
                              :placeholder="item.type.placeholder||`请输入${itemLabel(item)}`"
                              :rows="item.type.rows"
                              :show-password="item.type.showPassword"
                              :show-word-limit="item.type.showWordLimit"
                              :size="item.type.size"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                              :type="item.type.type"
                            >
                              <template #[item.type.slot]>{{ item.type.unit || '' }}</template>
                            </el-input>
                          </template>
                          <!--数字输入框-->
                          <template v-else-if="item.type?.name === TypeName.inputNumber">
                            <el-input-number
                              v-model="formRef[item.prop]"
                              :controls="item.type.controls"
                              :disabled="shouldDisabled(item)"
                              :max="item.type.max"
                              :min="item.type.min"
                              :placeholder="item.type.placeholder||`请输入${itemLabel(item)}`"
                              :precision="item.type.precision"
                              :size="item.type.size"
                              :step="item.type.step"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                              controls-position="right"
                            />
                          </template>
                          <!--金额输入框-->
                          <template v-else-if="item.type?.name === TypeName.money">
                            <el-input
                              v-model="formRef[item.prop]"
                              :disabled="shouldDisabled(item)"
                              :placeholder="item.type.placeholder||`请输入${itemLabel(item)}`"
                              :size="item.type.size"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                            >
                              <template #append>{{ item.type.unit || '元' }}</template>
                            </el-input>
                          </template>
                          <!--单选框-->
                          <template v-else-if="item.type?.name === TypeName.radio">
                            <el-radio-group
                              v-model="formRef[item.prop]"
                              :disabled="shouldDisabled(item)"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                            >
                              <el-radio
                                v-for="option in item.type.options"
                                :key="option[item.type.value]"
                                :disabled="shouldDisabled(option)"
                                :label="option[item.type.value]"
                                :value="option[item.type.value]"
                              >
                                {{ option[item.type.label] }}
                              </el-radio>
                            </el-radio-group>
                          </template>
                          <!--日期时间选择器-->
                          <template v-else-if="item.type?.name === TypeName.datetime">
                            <el-date-picker
                              v-model="formRef[item.prop]"
                              :disabled="shouldDisabled(item)"
                              :placeholder="`请选择${itemLabel(item)}`"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                              type="datetime"
                              value-format="YYYY-MM-DD HH:mm:ss"
                            />
                          </template>
                          <!--日期时间范围选择器-->
                          <template v-else-if="item.type?.name === TypeName.datetimerange">
                            <el-date-picker
                              v-model="formRef[item.prop.join('_')]"
                              :disabled="shouldDisabled(item)"
                              :end-placeholder="`结束${itemLabel(item)}`"
                              :start-placeholder="`开始${itemLabel(item)}`"
                              :style="item.style||{width:styleValue(item.width,'%','100%'),height:styleValue(item.height,'rem')}"
                              clearable
                              range-separator="至"
                              type="datetimerange"
                              value-format="YYYY-MM-DD HH:mm:ss"
                            />
                          </template>
                          <!--选择器-->
                          <template v-else-if="item.type?.name === TypeName.select">
                            <el-select
                              v-model="formRef[item.prop]"
                              :disabled="shouldDisabled(item)"
                              :loading="item.type.loading"
                              :placeholder="`--请选择${itemLabel(item)}--`"
                              :remote="item.type.remote"
                              :remote-method="keyWord=>{remoteSearch(keyWord,item)}"
                              :style="item.style||{width:styleValue(item.width,'%'),height:styleValue(item.height,'rem')}"
                              :clearable="item.required===false"
                              filterable
                              @change="res=>{if(item.change)item.change(formRef,res)}"
                              @focus="getRemoteData(item)"
                            >
                              <el-option
                                v-for="option in item.type.options||selectRemoteDataMapRef[item.prop]?.options"
                                :key="option[item.type.key]"
                                :disabled="shouldDisabled(option)"
                                :label="option[item.type.label]"
                                :value="option[item.type.value]"
                              >
                                <slot :name="item.prop" :row="option" />
                              </el-option>
                            </el-select>
                          </template>
                          <!--图片上传-->
                          <template v-else-if="item.type?.name === TypeName.imageUpload">
                            <image-cropper-upload
                              :ref="item.prop"
                              :current-img="formRef[item.prop+'_Link']"
                              :disabled="shouldDisabled(item)"
                              :img-height="item.type.height||item.height||150"
                              :img-width="item.type.width||item.width||150"
                              :upload-path="item.type.uploadPath"
                              @img-upload="link=>{
                                formRef[item.prop+'_Link']=link
                                formRef[item.prop]=link
                              }"
                            />
                          </template>
                        </el-form-item>
                      </el-col>
                    </template>
                  </el-row>
                </template>

              </template>
            </div>
          </template>
        </el-form>
      </div>
    </el-scrollbar>
    <!--弹出层底部-->
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
        <el-button @click="()=>close(undefined,'qx')">取 消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
// 表单类型枚举
import TypeName from '@/layouts/content/FormDialog/TypeName'
import { priceToFen, priceToYuan } from '@/utils'
import ImageCropperUpload from '@/components/Tools/Upload/ImageCropperUpload.vue'
import { computed } from 'vue'

defineOptions({
  name: 'FormDialog'
})
/**
 * 组件发出的事件及其描述：
 * - tabChange: 当标签页变更时触发,传递当前标签页索引
 * - submit: 当表单提交时触发,传递表单数据
 * - update: 执行更新时触发,传递表单数据
 * - save: 执行保存时触发,传递表单数据
 * - reset: 重置表单数据时触发
 */
const emits = defineEmits(['tabChange', 'submit', 'update', 'save', 'reset'])
const props = defineProps({
  // 弹出层宽度
  width: {
    type: [
      String,
      Number
    ],
    default: 50
  },
  // 弹出层高度
  height: {
    type: [
      String,
      Number
    ],
    default: 60
  },
  // 表单label宽度
  labelWidth: {
    type: [
      String,
      Number
    ],
    default: '5rem'
  },
  // 是否居中
  center: {
    type: Boolean,
    default: false
  },
  // 弹出层距离顶部距离
  top: {
    type: [Number, String],
    default: 6
  },
  // 点击弹出层外关闭弹窗
  modalClose: {
    type: Boolean,
    default: true
  },
  // 表单配置项
  formProps: {
    type: Array,
    default: () => []
  },
  // 对提交数据进行校验判断是更新还是保存
  checkIsUpdate: {
    type: Function,
    default: res => res.id !== undefined
  },
  // 多表单配置项
  tabs: {
    type: Array,
    default: () => []
  },
  // 设置当前tabs索引,返回值要求为,tabs索引
  setCurTabIndex: {
    type: Function,
    default: () => 0
  },
  // 自定义表单校验函数
  verifyRule: {
    type: Function,
    default: () => {}
  },
  // 是否开启提交遮罩层
  openSubLoad: {
    type: Boolean,
    default: true
  },
  // 提交成功提示信息
  submitTipMsg: {
    type: Boolean,
    default: true
  }
})
const pageThis = getCurrentInstance()
//----------------------------------------------------------------------
// 显示弹出层
const showDialogRef = ref(false)
// 表单数据
const formRef = ref({})
// 下拉框请求数据集
const selectRemoteDataMapRef = ref({})
// 弹出层标题
const formTitle = ref('')
// 储存formProps数据
const formItemsRef = ref([])
// 当前tabs索引
const curTabIndexRef = ref('')
// 打开的tabs索引
const openTabIndexRef = ref('0')

// 监听form变化
let watchForm = true
// 旧表单数据
let previousForm = {}
// 当前dialog是否是编辑模式
let dialogIsNew = false
// 是否开始错误查找
let errorFind = false
//-----------------------------------------------------------
//  拷贝formProps表单项防止直接修改props数据
watch(() => props.formProps, value => {
  if (props.tabs.length === 0) formItemsRef.value = [[...value].map(item => Array.isArray(item) ? item : [item])]
  else {
    formItemsRef.value = [...value].map(item => item.map(item2 => Array.isArray(item2) ? item2 : [item2]))
    formItemsRef.value.length = props.tabs.length
  }
}, {deep: true, immediate: true})
//  监听tabs数据变化,改变时重置索引
watch(() => props.tabs, () => curTabIndexRef.value = '0')
// 监听tabs切换事件
watch(curTabIndexRef, value => {
  emits('tabChange', Number(value))
  openTabIndexRef.value = String(value)
})
// 监听表单数据变化,触发计算属性
watch(formRef, newValue => {
  if (!watchForm) return watchForm = true
  const oldValue = deepClone(previousForm)
  Object.freeze(oldValue)
  const changedKeys = Object.keys(newValue).filter(key => newValue[key] !== oldValue[key])
  // 表单数据发生变化,进行计算属性更新
  if (changedKeys.length) {
    computedValues.value.forEach(item => {
      const needUpdate = item.relyProps.some(value => changedKeys.includes(value))
      if (needUpdate) item.func(newValue, oldValue, changeForm)
    })
  }
  previousForm = {...newValue}
}, {deep: true})
onMounted(() => {
  // 添加监听视口变化手动更新scrollbar
  window.addEventListener('resize', () => pageThis.refs.scrollbar?.update())
})
onBeforeUnmount(() => {
  // 移除监听视口变化
  window.removeEventListener('resize', () => pageThis.refs.scrollbar?.update())
})

// 计算表单校验规则
const computedRules = computed(() => {
  const rules = {}
  formItemsRef.value.forEach((group, groupIndex) => {
    // 当前组不显示,跳过
    if (props.tabs.length > 0 && (!shouldShowItem(props.tabs[groupIndex] || !weakShowItem(props.tabs[groupIndex])))) return
    // 当前组显示,为组内表单项添加校验规则
    group.forEach(formItem => formItem.forEach(item => {
      // 跳过非表单项数据
      if (item.slot || !item.prop) return
      const isRequired = item.required || item.required === undefined
      const isMoney = item.type && item.type.name === TypeName.money
      const propKey = itemProp(item)
      // 当前表单项是否显示
      if (shouldShowItem(item) || weakShowItem(item)) {
        rules[propKey] = []
        // 值改变立即校验类型
        const changeValidateType = [TypeName.select, TypeName.imageUpload]
        // 创建必填项校验规则
        if (isRequired) rules[propKey].push({
          required: true,
          message: `${ itemLabel(item) }不能为空`,
          trigger: changeValidateType.includes(item.type?.name) ?
              'change' :
              item.changeValidate ? 'change' : 'blur'
        })
        // 金钱类型校验规则
        if (isMoney) rules[propKey].push({
          validator: $formVerify.money(itemLabel(item), item.type.max, item.type.min),
          trigger: 'blur'
        })
        // 自定义表单项校验规则
        if (item.rule) {
          if (rules[propKey].length === 0) rules[propKey] = verifyRulePack(item.rule)
          else rules[propKey] = rules[propKey].concat(verifyRulePack(item.rule))
        }
      }
    }))
  })
  return rules
})

// 计算初始表单数据
const computedInitialForm = computed(() => {
  const initial = {}
  formItemsRef.value.forEach(group => group.forEach(formItem => formItem.forEach(item => {
    // 跳过非表单项数据
    if (!item.prop) return
    if (Array.isArray(item.prop)) item.prop.forEach((prop, index) => initial[prop] = Array.isArray(item.default) ? item.default[index] : undefined)
    else if (item.type?.name === TypeName.datetimerange) {
      initial[item.prop + '_Link'] = item.default !== undefined ? item.default : undefined
      initial[item.prop] = item.default !== undefined ? item.default : undefined
    } else initial[item.prop] = item.default !== undefined ? item.default : undefined
  })))
  return initial
})

// 将formProps进行数据初始化并分组
const computedGroupedFormProps = computed(() => {
  if (props.tabs.length === 0) return null
  else {
    const grouped = []
    formItemsRef.value.forEach((group, groupIndex) => group.forEach(formItem => formItem.forEach(item => {
      // 跳过非表单项数据
      if (!grouped[groupIndex]) grouped[groupIndex] = []
      if (!item.prop) return
      grouped[groupIndex].push(itemProp(item))
    })))
    return grouped
  }
})


const computedValues = computed(() => {
  const computed = []
  const computedKeys = formProps => {
    formProps.forEach(item => {
      if (Array.isArray(item)) computedKeys(item)
      else if (item.type?.name === TypeName.computed && typeof item.type.func === 'function') {
        computed.push({
          func: item.type.func,
          relyProps: item.type.relyProps
        })
      }
    })
  }
  computedKeys(props.formProps)
  return computed
})


const isNotNaN = value => !(value === undefined || value === null || value === '')
// 创建一个方法来更新 form 而不触发 watch
const changeForm = func => {
  watchForm = false
  // 调用 func 更新 form
  const formOld = deepClone(formRef.value)
  const formNew = deepClone(formRef.value)
  func(formNew)
  const changedKeys = Object.keys(formNew).filter(key => formNew[key] !== formOld[key])
  changedKeys.forEach(key => {
    formRef.value[key] = formNew[key]
    previousForm[key] = formNew[key]
  })
}
const itemProp = item => Array.isArray(item.prop) ? item.prop.join('_') : item.prop

const itemLabel = item => {
  if (typeof item.label === 'function') return item.label({...formRef.value, dialogIsNew: dialogIsNew})
  else return item.label
}

const deepClone = (obj, hash = new WeakMap()) => {
  // 处理 null，undefined 和非对象情况
  if (obj === null || typeof obj !== 'object') return obj
  // 处理日期类型
  if (obj instanceof Date) return new Date(obj)
  // 处理正则表达式类型
  if (obj instanceof RegExp) return new RegExp(obj)
  // 处理数组和普通对象
  if (obj instanceof Array || obj instanceof Object) {
    // 检查是否有循环引用
    if (hash.has(obj)) return hash.get(obj)
    // 创建一个新的容器
    const cloneObj = new obj.constructor()
    hash.set(obj, cloneObj)
    // 递归拷贝所有属性
    for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) cloneObj[key] = deepClone(obj[key], hash)
    return cloneObj
  }
  // 处理Map
  if (obj instanceof Map) {
    const newMap = new Map()
    obj.forEach((value, key) => {
      newMap.set(key, deepClone(value, hash))
    })
    return newMap
  }

  // 处理Set
  if (obj instanceof Set) {
    const newSet = new Set()
    obj.forEach(value => {
      newSet.add(deepClone(value, hash))
    })
    return newSet
  }
  return obj
}
// 远程搜索用于--TypeName.select--类型
const remoteSearch = async (keyWord, item) => {
  if (!selectRemoteDataMapRef.value[item.prop]) selectRemoteDataMapRef.value[item.prop] = {
    options: [],
    loading: true
  }

  const selectObj = selectRemoteDataMapRef.value[item.prop]
  if (keyWord !== '') {
    selectObj.loading = true
    // 执行远程搜索逻辑 item.type.remoteMethod(keyWord)
    selectObj.options = await new Promise(resolve => item.type.remoteMethod(keyWord, resolve))
    selectObj.loading = false
  } else {
    selectObj.options = []
  }
}
// 获取远程数据列表
const getRemoteData = async (item, form = {}, isInit = false) => {
  if (item.type.remoteMethod === undefined) return
  if (!selectRemoteDataMapRef.value[item.prop]) selectRemoteDataMapRef.value[item.prop] = {
    options: [],
    loading: true
  }

  const selectObj = selectRemoteDataMapRef.value[item.prop]
  if (selectObj.options.length === 0 || isInit) {
    selectObj.loading = true
    selectObj.options = await new Promise(resolve => item.type.remoteMethod(resolve, form))
    selectObj.loading = false
  }
}
/**
 * 打开弹窗
 * @param title
 * @param echoData 回显数据
 */
const open = (title, echoData) => {
  if (typeof title !== "string") return $xl.modal.msgError('表单标题错误')
  formTitle.value = title
  // 设置dialog模式
  dialogIsNew = !echoData
  // 回显datetimerange类型数据函数
  const initializeForm = (data) => {
    const formData = {...data}
    formItemsRef.value.forEach(group => group.forEach(formItem => formItem.forEach(item => {
      // 跳过非表单项数据
      if (!item.type) return
      // 处理特殊类型数据
      switch (item.type.name) {
        case TypeName.datetimerange: {
          if (!Array.isArray(item.prop)) return
          const start = formData[item.prop[0]]
          const end = formData[item.prop[1]]
          return formData[item.prop.join('_')] = start && end ? [
            start,
            end
          ] : undefined
        }
        case TypeName.money:
          return formData[item.prop] = isNotNaN(formData[item.prop]) ? priceToYuan(formData[item.prop]) : undefined
        case TypeName.imageUpload:
          return formData[item.prop + '_Link'] = isNotNaN(formData[item.prop]) ? formData[item.prop] : undefined
        case TypeName.select:
          return getRemoteData(item, data, true)
      }
    })))
    return formData
  }
  // 填充表单数据
  if (echoData && Object.keys(echoData).length) formRef.value = deepClone({...computedInitialForm.value, ...initializeForm(echoData)})
  else formRef.value = deepClone(initializeForm(computedInitialForm.value))
  // 用于首次打开表单表单时,表单数据为空,进行数据初始化
  // if (!Object.keys(formRef.value).length) formRef.value = deepClone(initializeForm(computedInitialForm))

  // 设置当前tabs索引
  if (props.tabs.length > 0) {
    // 获取到用户设置的tabs索引
    const showIndex = props.setCurTabIndex({...formRef.value, dialogIsNew: dialogIsNew})
    // 当前索引错误,则设置为第一个有效索引
    if (showIndex === undefined || showIndex < 0 || showIndex >= props.tabs.length) {
      curTabIndexRef.value = String(props.tabs.findIndex(item => shouldShowItem(item) && weakShowItem(item)))
    } else {
      // 判断当前索引是否显示,如果不显示,则设置为第一个有效索引
      if (shouldShowItem(props.tabs[showIndex]) && weakShowItem(props.tabs[showIndex])) curTabIndexRef.value = String(showIndex)
      else curTabIndexRef.value = String(props.tabs.findIndex(item => shouldShowItem(item) && weakShowItem(item)))
    }
  }

  showDialogRef.value = true
}
// 判断该表单项是否显示
const shouldShowItem = item => objPropIsTrue(item, 'show')
// 判断该表单项是否显示
const weakShowItem = item => objPropIsTrue(item, 'weakShow')
// 判断该表单项是否不可编辑
const shouldDisabled = item => objPropIsTrue(item, 'disabled', false)
const objPropIsTrue = (obj, prop, isNullValue = true) => {
  // 如果属性不存在，默认为isNullValue
  if (obj[prop] === undefined) return isNullValue
  // 如果属性是布尔值，直接返回该值
  if (typeof obj[prop] === 'boolean') return obj[prop]
  // 如果属性是函数，执行该函数并返回结果
  if (typeof obj[prop] === 'function') return obj[prop]({...formRef.value, dialogIsNew: dialogIsNew, curTabIndex: curTabIndexRef.value})
  // 其他情况，默认不显示
  return !isNullValue
}
// 获取正确样式
const styleValue = (value, numberUnit, defaultValue = undefined) => {
  if (value === undefined || value === null) return defaultValue || null
  return typeof value === 'string' ? value : value + numberUnit
}
// 数据处理
const handleFormData = async (formData, submitData) => {
  const isUpdate = props.checkIsUpdate({...formData, ...submitData})
  const asyncData = []
  formItemsRef.value.forEach((group, groupIndex) => {
    // 当前组不显示,为组内表单项设置默认值
    if (props.tabs.length > 0 && !shouldShowItem(props.tabs[groupIndex])) {
      group.forEach(formItem => formItem.forEach(item => {
        // 跳过非表单项数据
        if (item.slot || !item.prop) return
        const isArrayProp = Array.isArray(item.prop)
        if (isArrayProp) item.prop.forEach((prop, index) => submitData[prop] = (Array.isArray(item.default) ? item.default[index] : isUpdate ? '' : undefined))
        else submitData[item.prop] = item.default !== undefined ? item.default : isUpdate ? '' : undefined
      }))
    }
    // 当前组显示,为组内表单项进行数据处理
    else {
      const isShowProp = {}
      group.forEach(formItem => formItem.forEach(item => {
        // 跳过非表单项数据
        if (item.slot || !item.prop) return
        const isArrayProp = Array.isArray(item.prop)
        const itemIsShow = shouldShowItem(item)
        // 处理显示的表单数据
        if (itemIsShow && item.type) {
          // 处理特殊类型数据
          switch (item.type.name) {
            case TypeName.datetimerange:
              if (!isArrayProp) break
              formData[item.prop[0]] = formData[item.prop.join('_')][0]
              formData[item.prop[1]] = formData[item.prop.join('_')][1]
              delete formData[item.prop.join('_')]
              break
            case TypeName.money:
              formData[item.prop] = priceToFen(formData[item.prop])
              break
            case TypeName.imageUpload:
              asyncData.push({...item, upLoadFunc: pageThis.refs[item.prop][0].uploadImage})
              delete formData[item.prop + '_Link']
              break
          }
        }
        // 提取数据并还原隐藏项默认值
        if (isArrayProp) item.prop.forEach((prop, index) => submitData[prop] = itemIsShow ? isShowProp[prop] = formData[prop] : (Array.isArray(item.default) ? item.default[index] : isUpdate ? '' : undefined))
        else submitData[item.prop] = itemIsShow ? isShowProp[item.prop] = formData[item.prop] : (item.default !== undefined ? item.default : isUpdate ? '' : undefined)
      }))
      // 将重新赋值已经显示的表单数据,防止隐藏项默认值覆盖显示的表单数据
      Object.keys(isShowProp).forEach(key => {
        if (isShowProp[key] !== undefined) submitData[key] = isShowProp[key]
      })
    }
  })
  // 自定义表单校验
  if (await props.verifyRule(submitData, openTab)) return false
  // 表单提交loading
  if (props.openSubLoad) $xl.modal.openLoading("正在保存...")
  // 处理资源上传
  for (let i = 0 ; i < asyncData.length ; i++) {
    const item = await asyncData[i]
    if (formData[item.prop]) submitData[item.prop] = await item.upLoadFunc()
    // 处理资源上传失败情况
    else {
      if (item.required) return item
    }
  }
}
// 提交表单
const handleSubmit = async () => {
  errorFind = true
  await pageThis.refs.formTemplateRef.validate(async valid => {
    // 表单校验失败
    if (!valid) return
    // 表单校验成功
    errorFind = false
    // 防止直接修改form数据,导致数据同步问题
    const formData = {...formRef.value}
    const submitData = {}
    // 处理表单数据
    const result = await handleFormData(formData, submitData)
    // 自定义校验失败
    if (typeof result === 'boolean' && !result) return

    // 资源上传失败
    if (typeof result === 'object' && result) {
      $xl.modal.closeLoading()
      return $xl.modal.msgError(`${ result.label }上传失败!!!`)
    }
    if (props.checkIsUpdate({...formData, ...submitData})) emits('update', {...formData, ...submitData})
    else emits('save', {...formData, ...submitData})
    emits('submit', {...formData, ...submitData})
  })
}

// 创建自定义表单校验
const verifyRulePack = rules => rules.map(ruleItem => {
  if (typeof ruleItem.validator === 'function') {
    const originalValidator = ruleItem.validator
    ruleItem = {...ruleItem, validator: (rule, value, callback) => originalValidator(formRef.value)(rule, value, callback)}
  }
  return ruleItem
})

// 表单校验失败回调
const handleValidate = (prop, isValid) => {
  if (!isValid && computedGroupedFormProps.value && errorFind) {
    errorFind = false
    console.log('校验未通过属性:' + prop)
    curTabIndexRef.value = String(computedGroupedFormProps.value.findIndex(item => item.includes(prop)))
  }
}
// 关闭弹窗并重置表单(按钮)
const close = (error, type) => {
  $xl.modal.closeLoading()
  if (error !== undefined) return $xl.modal.msgError('操作失败！' + error)
  else if (type !== 'qx' && props.submitTipMsg) $xl.modal.msgSuccess('操作成功！')
  showDialogRef.value = false
}
// 重置表单数据和表单校验
const resetForm = () => {
  // 延迟重置表单,因为关闭弹窗时有延迟动画,导致弹窗未完全关闭时,表单显示的数值会被重置
  setTimeout(() => {
    showDialogRef.value = false
    formRef.value = deepClone({...computedInitialForm.value})
    selectRemoteDataMapRef.value = {}
    curTabIndexRef.value = ''
    openTabIndexRef.value = '0'
    errorFind = false
    watchForm = true
    pageThis.refs.formTemplateRef?.resetFields()
    emits('reset')
  }, 200)
}
/** *********************************************************************************/
// 对外开放修改form数据的方法
const setFormData = func => {
  if (typeof func === 'function') func(formRef.value)
  if (typeof func === 'object') Object.assign(formRef.value, func)
}
/**
 * 设置当前tabs索引
 * @param index - number 索引值
 */
const openTab = index => {
  if (props.tabs.length > 0 && typeof index === 'number') curTabIndexRef.value = String(index)
  return true
}
/**
 * 清除表单校验,如果prop为空则清除所有表单校验
 * @param prop
 */
const clearValidate = prop => pageThis.refs.formTemplateRef?.clearValidate(prop)
/**
 * 对部分表单字段进行校验的方法
 */
const validateField = prop => {
  nextTick(() => {
    const field = pageThis.refs.formTemplateRef?.fields.find(field => field.prop === prop)
    if (field) pageThis.refs.formTemplateRef?.validateField(prop)
  })
}

defineExpose({
  open,
  close,
  setFormData,
  openTab,
  clearValidate,
  validateField,
})
</script>


<style lang="scss" scoped>
.el-dialog__wrapper {
  margin: 0 0 -1rem 0 !important;
}

.dialog-title {
  display: flex;
  align-items: center;
  line-height: 1.5rem;
  font-size: 1.125rem;
  color: #303133;
}

//导致滚动条占宽度
//::v-deep .el-scrollbar__wrap {
//  overflow: auto;
//}

:deep( .el-tabs__header) {
  margin: 0;
}

:deep( .el-col) {
  padding-right: 1rem !important;
}

:deep(.el-tabs--card > .el-tabs__header) {
  width: 99%;
}

.edit-form {
  border: 1px solid #dfe4ed;
  border-radius: 4px;
}

.tabs-form {
  outline: 1px solid #dfe4ed;
  margin-left: 1px;
  border-bottom-left-radius: 0.7%;
  border-bottom-right-radius: 0.7%;
  border-top-right-radius: 0.7%;

}

.have-scrollbar {
  margin: 1rem 1.8rem 1rem 1rem;
  box-sizing: border-box;
}

.no-scrollbar {
  :deep(.el-form-item) {
    margin-bottom: 0;
  }
}
:deep(.el-form-item__label) {
  font-weight: bolder;
}
</style>
